// Compiler.cpp : Defines the entry point for the console application.
//
#include "Basic.h"
#include "Lexer.h"
#include "Parser.h"
#include "LibIO.h"
#include "SyntaxVisitors.h"
#include "CodeGenerator.h"
#include "IL/CFG.h"
#include "IL/Optimization.h"
#include "IL/x86CodeGen.h"

using namespace CoreLib::Basic;
using namespace SimpleC::Compiler;

void PrintError(List<CompileError> &errorList)
{
	if (errorList.Count() > 0)
	{
		for (int i = 0; i<errorList.Count(); i++)
		{
			printf("%s line %d, col %d\n", errorList[i].Message.ToMultiByteString(), errorList[i].Line, errorList[i].Col);
		}
	}
}

typedef int (__stdcall *IntFunc)();

int __stdcall sum()
{
	int sum = 0;
	for (int i = 1; i<9; i++)
		for (int j = 0; j<9; j++)
			if ((i*j%2) == 0)
				sum= sum + i*j; 
	return sum;
}

int __stdcall hannoi(int level)
{
	int rs = 0;
	if (level==1)
		return 1;
	return hannoi(level-1)*2 + 1;
}

IntFunc MakeFunc(List<unsigned char> & code)
{
	char * rs = (char *)VirtualAlloc(0, code.Count(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	memcpy(rs, code.Buffer(), code.Count());
	FlushInstructionCache(GetCurrentProcess(), rs, code.Count());
	return (IntFunc)(rs);
}

int wmain(int argc, wchar_t* argv[])
{
	if (argc == 2)
	{
		String fileName = argv[1];
		auto source = File::ReadAllText(fileName);
		Lexer lexer;
		List<CompileError> errorList;
		
		auto tokens = lexer.Parse(argv[1], source, errorList);
		
		Parser parser(tokens, errorList, fileName);
		auto programSyntaxNode = parser.Parse();
		if (errorList.Count() > 0)
		{
			PrintError(errorList);
			return 0;
		}

		RefPtr<SyntaxVisitor> visitor = CreateSemanticsVisitor(errorList);
		programSyntaxNode->Accept(visitor.Ptr());
		if (errorList.Count() > 0)
		{
			PrintError(errorList);
			return 0;
		}

		SimpleC::Compiler::CodeGenerator gen;
		programSyntaxNode->Accept(&gen);
		fileName = Path::ReplaceExt(fileName, L"code");
		gen.CompiledCode->Dump(fileName);
		RefPtr<IntraProcOptimizer> preSsaOptmizer = CreateControlFlowCleanupOptimizer();
		RefPtr<IntraProcOptimizer> optimizer = 
			new CompoundOptimizer
			(
				new IterateOptimizer
				(
					new CompoundOptimizer
					(
						CreateUselessInstructionOptimizer(),
						CreateVariableCleanupOptimizer(),
						CreateDeadCodeOptimizer(),
						CreateControlFlowCleanupOptimizer(),
						CreateConstIndirectionRemovalOptimizer(),
						CreateVariableCleanupOptimizer()
					)
				),
				CreatePeepHoleOptimizer()
			);
		RefPtr<IntraProcOptimizer> regAllocator = 
			new CompoundOptimizer
			(
				CreateBranchFuseOptimizer(),
				CreateOutOfSSA_Transform(),
				CreateRegisterAllocator()
			);
		for (auto & func:gen.CompiledCode->Functions)
		{
			RefPtr<ControlFlowGraph> graph = ControlFlowGraph::FromCode(func);
			graph->Dump(Path::ReplaceExt(fileName, L"cfg") + L"." + func.Name + L"_original.cfgdump");
			graph = preSsaOptmizer->Optimize(graph).Program;
			graph->ConvertToSSA();
			graph->Dump(Path::ReplaceExt(fileName, L"cfg") + L"." + func.Name + L"_ssa.cfgdump");
			graph = optimizer->Optimize(graph).Program;
			graph->Dump(Path::ReplaceExt(fileName, L"cfg") + L"." + func.Name + L"_optimize.cfgdump");
			graph = regAllocator->Optimize(graph).Program;
			graph->ToCode(func);
			graph->Dump(Path::ReplaceExt(fileName, L"cfg") + L"." + func.Name + L"_final.cfgdump");
		}
		RefPtr<X86CodeGenerator> x86Gen = CreateX86CodeGenerator();
		auto program = x86Gen->GenerateCode(gen.CompiledCode.Ptr());
		for (auto & func : program.Functions)
		{
			func.Dump(Path::ReplaceExt(fileName, L"asm") + L"." + func.Name + L".txt");
		}
		auto assembly = program.Link();
		
		auto exe = assembly.CreateMemoryExecutable();
		
		IntFunc f = (IntFunc)exe.Buffer;
		LARGE_INTEGER c1, c2, freq;
		int rs, rs2;
		QueryPerformanceCounter(&c1);
		for (int i = 0; i<1000; i++)
			rs = f();
		QueryPerformanceCounter(&c2);
		QueryPerformanceFrequency(&freq);
		double time1 = (c2.QuadPart-c1.QuadPart)/(double)(freq.QuadPart);
		printf("time1: %f\n", time1);
		QueryPerformanceCounter(&c1);
		for (int i = 0; i<1000; i++)
			rs2 = sum();
		QueryPerformanceCounter(&c2);
		double time2 = (c2.QuadPart-c1.QuadPart)/(double)(freq.QuadPart);
		printf("time2: %f\n", time2);
		printf("result is %d, %d\n", rs, rs2);
		PrintError(errorList);
	}
	return 0;
}

